package org.springframework.core.convert.support;

import cn.hutool.core.convert.BasicType;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.core.convert.converter.ConverterRegistry;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

/**
 * 实现了ConversionService类 里面封装了许多GenericConverter转换器 可以根据不同的类型转化来自动取出
 */
public class GenericConversionService implements ConversionService, ConverterRegistry {
    //存放GenericConverter的集合 是通过转换的类型来作为key的
    private Map<ConvertiblePair, GenericConverter> converters = new HashMap<>();

    /**
     * 判断是否可以转换
     * @param sourceType
     * @param targetType
     * @return
     */
    @Override
    public boolean canConvert(Class<?> sourceType, Class<?> targetType) {
        //根据目标类型和源类型获取转换器
        GenericConverter converter = getConverter(sourceType, targetType);
        return converter != null;
    }

    /**
     * 获取转换器 执行转换逻辑
     * @param source
     * @param targetType
     * @return
     * @param <T>
     */
    @Override
    public <T> T convert(Object source, Class<T> targetType) {
        Class<?> sourceType = source.getClass();
        //将目标类型进行包装了一下转换为包装类吧
        targetType = (Class<T>) BasicType.wrap(targetType);
        GenericConverter converter = getConverter(sourceType, targetType);
        return (T) converter.convert(source, sourceType, targetType);
    }

    /**
     * 增加转换器到集合里存放
     * @param converter
     */
    @Override
    public void addConverter(Converter<?, ?> converter) {
        //先获取转换器转换的类型 作为key存储
        ConvertiblePair typeInfo = getRequiredTypeInfo(converter);
        //将转换的类型和转换器封装到适配器里 感觉有点多此一举了 为什么不直接添加到集合里呢 可能是类型不匹配的问题吧
        ConverterAdapter converterAdapter = new ConverterAdapter(typeInfo, converter);
        //实际上这里遍历的集合数量只有一个的。。 感觉遍历也多此一举了
        for (ConvertiblePair convertibleType : converterAdapter.getConvertibleTypes()) {
            converters.put(convertibleType, converterAdapter);
        }
    }

    /**
     * 增加转换器工厂到集合里存放 和上面的逻辑一致
     * @param converterFactory
     */
    @Override
    public void addConverterFactory(ConverterFactory<?, ?> converterFactory) {
        ConvertiblePair typeInfo = getRequiredTypeInfo(converterFactory);
        ConverterFactoryAdapter converterFactoryAdapter = new ConverterFactoryAdapter(typeInfo, converterFactory);
        for (ConvertiblePair convertibleType : converterFactoryAdapter.getConvertibleTypes()) {
            converters.put(convertibleType, converterFactoryAdapter);
        }
    }

    /**
     * 增加GenericConverter到集合里 因为它里面已经封装了转换的类型和转换器 所以不需要再封装到适配器里了
     * @param converter
     */
    @Override
    public void addConverter(GenericConverter converter) {
        //这里面都封装好了类型和转换器了 不需要通过适配器再封装了
        for (ConvertiblePair convertibleType : converter.getConvertibleTypes()) {
            converters.put(convertibleType, converter);
        }
    }

    /**
     * 获取转换器的类型信息
     * @param object
     * @return
     */
    private ConvertiblePair getRequiredTypeInfo(Object object) {
        //通过反射获取类型信息
        Type[] types = object.getClass().getGenericInterfaces();
        ParameterizedType parameterized = (ParameterizedType) types[0];
        Type[] actualTypeArguments = parameterized.getActualTypeArguments();
        Class sourceType = (Class) actualTypeArguments[0];
        Class targetType = (Class) actualTypeArguments[1];
        return new ConvertiblePair(sourceType, targetType);
    }


    /**
     * 根据源类型和目标类型 以及他们的所有父类 来获取转换器
     * @param sourceType
     * @param targetType
     * @return
     */
    protected GenericConverter getConverter(Class<?> sourceType, Class<?> targetType) {
        //获取源类型的所有父类信息
        List<Class<?>> sourceCandidates = getClassHierarchy(sourceType);
        //获取目标类型的所有父类信息
        List<Class<?>> targetCandidates = getClassHierarchy(targetType);

        //组合遍历所有情况 然后获取转换器
        for (Class<?> sourceCandidate : sourceCandidates) {
            for (Class<?> targetCandidate : targetCandidates) {
                ConvertiblePair convertiblePair = new ConvertiblePair(sourceCandidate, targetCandidate);
                GenericConverter converter = converters.get(convertiblePair);
                if (converter != null) {
                    return converter;
                }
            }
        }
        return null;
    }


    /**
     * 这个方法是获取当前类的所有父类信息
     * @param clazz
     * @return
     */
    private List<Class<?>> getClassHierarchy(Class<?> clazz) {
        List<Class<?>> hierarchy = new ArrayList<>();
        //原始类转为包装类
        clazz = BasicType.wrap(clazz);
        while (clazz != null) {
            hierarchy.add(clazz);
            clazz = clazz.getSuperclass();
        }
        return hierarchy;
    }


    /**
     * 内部的Converter的适配器 负责封装好类型信息和converter转换器
     */
    private final class ConverterAdapter implements GenericConverter {

        private final ConvertiblePair typeInfo;

        private final Converter<Object, Object> converter;

        public ConverterAdapter(ConvertiblePair typeInfo, Converter<?, ?> converter) {
            this.typeInfo = typeInfo;
            this.converter = (Converter<Object, Object>) converter;
        }

        /**
         * 本质上也就是返回了一个类型信息
         * @return
         */
        @Override
        public Set<ConvertiblePair> getConvertibleTypes() {
            return Collections.singleton(typeInfo);
        }

        @Override
        public Object convert(Object source, Class sourceType, Class targetType) {
            return converter.convert(source);
        }
    }

    /**
     * 内部的ConverterFactory的适配器 负责封装好类型信息和ConverterFactory转换器
     */
    private final class ConverterFactoryAdapter implements GenericConverter {

        private final ConvertiblePair typeInfo;

        private final ConverterFactory<Object, Object> converterFactory;

        public ConverterFactoryAdapter(ConvertiblePair typeInfo, ConverterFactory<?, ?> converterFactory) {
            this.typeInfo = typeInfo;
            this.converterFactory = (ConverterFactory<Object, Object>) converterFactory;
        }

        @Override
        public Set<ConvertiblePair> getConvertibleTypes() {
            return Collections.singleton(typeInfo);
        }

        @Override
        public Object convert(Object source, Class sourceType, Class targetType) {
            return converterFactory.getConverter(targetType).convert(source);
        }
    }


}
